<?php declare(strict_types=1);
/**
 * @author      xianganyall <xianganyall@gmail.com>
 * @copyright   2023-2025 owner
 **/

namespace Srv\Libs\Frame;

use Srv\Libs\Boot;
use Srv\Libs\Common\CommJson;
use Srv\Libs\Common\CommString;
use Srv\Libs\ConfModel\CacheConfGroup;
use Srv\Libs\ConfModel\DbConfGroup;
use Srv\Libs\ConfModel\LangConf;
use Srv\Libs\ConfModel\LogsConf;
use Srv\Libs\ConfModel\NetworkConfGroup;
use Srv\Libs\ConfModel\PhpConf;
use Srv\Libs\ConfModel\ProjectConf;
use Srv\Libs\ConfModel\ResponseConf;
use Srv\Libs\ConfModel\RouteConfGroup;
use Srv\Libs\ConfModel\SmartyConf;
use Srv\Libs\ConfModel\TokenKeyConf;
use Srv\Libs\ConfModel\WebSocketConf;

final class Conf
{
    private static array $confAllDataList                   = [];           // 配置全部数据列表
    private static array $cliArgvList                       = [];           // CLI参数列表
    private static ?ProjectConf $ProjectConf                = null;         // ProjectConf
    private static ?LogsConf $LogsConf                      = null;         // LogsConf
    private static ?LangConf $LangConf                      = null;         // LangConf
    private static ?PhpConf $PhpConf                        = null;         // PhpConf
    private static ?WebSocketConf $WebSocketConf            = null;         // WebSocketConf
    private static ?ResponseConf $ResponseConf              = null;         // ResponseConf
    private static ?TokenKeyConf $TokenKeyConf              = null;         // TokenKeyConf
    private static ?RouteConfGroup $RouteConfGroup          = null;         // RouteConfGroup
    private static ?CacheConfGroup $CacheConfGroup          = null;         // CacheConfGroup
    private static ?DbConfGroup $DbConfGroup                = null;         // DbConfGroup
    private static ?NetworkConfGroup $NetworkConfGroup      = null;         // NetworkConfGroup
    private static ?SmartyConf $SmartyConf                  = null;         // SmartyConf

    /**
     * @param string $envFile
     * @return bool
     * init
     */
    public static function init(string $envFile):bool
    {
        self::$confAllDataList  = [];
        $fileDataList           = @file($envFile);
        if(!is_array($fileDataList) || count($fileDataList) < 1) return false;
        $isStart                = false;
        foreach($fileDataList as $lineText){
            $lineText           = trim($lineText);
            if(!$isStart){
                if($lineText === '?>'){
                    $isStart    = true;
                    continue;
                }
                continue;
            }
            $lineTextLength     = strlen($lineText);
            if($lineTextLength < 2) continue;
            $firstCharCode      = ord(substr($lineText, 0, 1));
            if(!(($firstCharCode >= 97 && $firstCharCode <= 122) || ($firstCharCode >= 65 && $firstCharCode <= 90))) continue;
            $lineTextPos        = strpos($lineText, '=');
            if($lineTextPos < 1) continue;
            $confKey            = rtrim(substr($lineText, 0, $lineTextPos));
            if(preg_match('/^[\w\-.\[\]]+$/', $confKey)){
                $confVal        = trim(preg_replace('/\s*\/\/\s+.*$/', '', substr($lineText, $lineTextPos + 1)));
                if(substr($confKey, -2) === CommJson::JSON_DEFAULT_EMPTY_STRING){
                    self::$confAllDataList[substr($confKey, 0, -2)][] = $confVal;
                }else{
                    self::$confAllDataList[$confKey] = $confVal;
                }
            }
        }
        self::$ProjectConf      = null;
        self::$LogsConf         = null;
        self::$LangConf         = null;
        self::$PhpConf          = null;
        self::$WebSocketConf    = null;
        self::$ResponseConf     = null;
        self::$TokenKeyConf     = null;
        self::$RouteConfGroup   = null;
        self::$CacheConfGroup   = null;
        self::$DbConfGroup      = null;
        self::$SmartyConf       = null;
        return true;
    }

    /**
     * @param string $confKey
     * @return bool
     * isExists
     */
    public static function isExists(string $confKey):bool
    {
        return isset(self::$confAllDataList[$confKey]);
    }

    /**
     * @param string $confKey
     * @param bool $isOrigin
     * @return string
     * getString
     */
    public static function getString(string $confKey, bool $isOrigin):string
    {
        $confVal    = strval(self::$confAllDataList[$confKey] ?? '');
        return $isOrigin ? $confVal : self::getResultString($confVal);
    }

    /**
     * @param string $confKey
     * @param bool $isOrigin
     * @return int
     * getInt
     */
    public static function getInt(string $confKey, bool $isOrigin):int
    {
        return intval(self::getString($confKey, $isOrigin));
    }

    /**
     * @param string $confKey
     * @param bool $isOrigin
     * @return float
     * getFloat
     */
    public static function getFloat(string $confKey, bool $isOrigin):float
    {
        return floatval(self::getString($confKey, $isOrigin));
    }

    /**
     * @param string $confKey
     * @param bool $isOrigin
     * @return bool
     * getBool
     */
    public static function getBool(string $confKey, bool $isOrigin):bool
    {
        return CommString::getBoolByString(trim(self::getString($confKey, $isOrigin)));
    }

    /**
     * @param string $confKey
     * @param bool $isOrigin
     * @return array
     * getArray
     */
    public static function getArray(string $confKey, bool $isOrigin):array
    {
        $confVal    = self::$confAllDataList[$confKey] ?? [];
        if(!is_array($confVal)) return [];
        if($isOrigin) return $confVal;
        return self::getResultArray($confVal);
    }

    /**
     * @param string $confKeyPrefix
     * @param string $confKeyName
     * @param string $confVal
     * @return array
     * getGroupNameListByValue
     */
    public static function getGroupNameListByValue(string $confKeyPrefix, string $confKeyName, string $confVal):array
    {
        $groupNameList          = [];
        $confKeyPrefixLength    = strlen($confKeyPrefix);
        if($confKeyPrefixLength < 1 || count(self::$confAllDataList) < 1) return $groupNameList;
        foreach(self::$confAllDataList as $confKeyItem => $confValItem){
            if($confValItem !== $confVal || substr($confKeyItem, 0, $confKeyPrefixLength) !== $confKeyPrefix) continue;
            $confKeyStr         = substr($confKeyItem, $confKeyPrefixLength);
            $confKeyPos         = strpos($confKeyStr, '.');
            if($confKeyPos > 0 && (strlen($confKeyName) < 1 || substr($confKeyStr, $confKeyPos + 1) === $confKeyName)) $groupNameList[] = substr($confKeyStr, 0, $confKeyPos);
        }
        return array_values(array_unique($groupNameList));
    }

    /**
     * @param string $confKeyPrefix
     * @return array
     * getGroupNameList
     */
    public static function getGroupNameList(string $confKeyPrefix):array
    {
        $groupNameList          = [];
        $confKeyPrefixLength    = strlen($confKeyPrefix);
        if($confKeyPrefixLength < 1 || count(self::$confAllDataList) < 1) return $groupNameList;
        foreach(self::$confAllDataList as $confKeyItem => $confValItem){
            if(substr($confKeyItem, 0, $confKeyPrefixLength) !== $confKeyPrefix) continue;
            $confKeyStr         = substr($confKeyItem, $confKeyPrefixLength);
            $confKeyPos         = strpos($confKeyStr, '.');
            if($confKeyPos > 0) $groupNameList[] = substr($confKeyStr, 0, $confKeyPos);
        }
        return array_values(array_unique($groupNameList));
    }

    /**
     * @param string $confKeyPrefix
     * @param bool $isOrigin
     * @param bool $isShort
     * @return array
     * getGroupList
     */
    public static function getGroupList(string $confKeyPrefix, bool $isOrigin, bool $isShort):array
    {
        $groupDataList          = [];
        $confKeyPrefixLength    = strlen($confKeyPrefix);
        if($confKeyPrefixLength < 1 || count(self::$confAllDataList) < 1) return $groupDataList;
        foreach(self::$confAllDataList as $confKeyItem => $confValItem){
            if(substr($confKeyItem, 0, $confKeyPrefixLength) !== $confKeyPrefix) continue;
            $confKey            = $isShort ? substr($confKeyItem, $confKeyPrefixLength) : $confKeyItem;
            (!$isShort || strlen($confKey) > 0) && $groupDataList[$confKey] = $isOrigin ? $confValItem : (is_string($confValItem) ? self::getResultString($confValItem) : (is_array($confValItem) ? self::getResultArray($confValItem) : $confValItem));
        }
        return $groupDataList;
    }

    /**
     * @param string $confKeyPrefix
     * @param bool $isOrigin
     * @param bool $isShort
     * @return array
     * getGroupArrayList
     */
    public static function getGroupArrayList(string $confKeyPrefix, bool $isOrigin, bool $isShort):array
    {
        $groupDataList              = [];
        $confKeyPrefixLength        = strlen($confKeyPrefix);
        if($confKeyPrefixLength < 1 || count(self::$confAllDataList) < 1) return $groupDataList;
        foreach(self::$confAllDataList as $confKeyItem => $confValItem){
            if(substr($confKeyItem, 0, $confKeyPrefixLength) !== $confKeyPrefix) continue;
            $confKey                = $isShort ? substr($confKeyItem, $confKeyPrefixLength) : $confKeyItem;
            if(strpos($confKey, '.') > 0){
                $groupTmp           = &$groupDataList;
                foreach(explode('.', $confKey) as $confKeyChild){
                    $confKeyChild   = trim($confKeyChild);
                    if(strlen($confKeyChild) < 1) break;
                    if(!isset($groupTmp[$confKeyChild])) $groupTmp[$confKeyChild] = [];
                    $groupTmp       = &$groupTmp[$confKeyChild];
                }
                $groupTmp           = $isOrigin ? $confValItem : (is_string($confValItem) ? self::getResultString($confValItem) : (is_array($confValItem) ? self::getResultArray($confValItem) : $confValItem));
            }else{
                $groupDataList[$confKey] = $isOrigin ? $confValItem : (is_string($confValItem) ? self::getResultString($confValItem) : (is_array($confValItem) ? self::getResultArray($confValItem) : $confValItem));
            }
        }
        return $groupDataList;
    }

    /**
     * @param array $arrayData
     * @return array
     * getResultArray
     */
    private static function getResultArray(array $arrayData):array
    {
        if(count($arrayData) < 1) return $arrayData;
        foreach ($arrayData as &$arrayItem){
            if(is_string($arrayItem)) $arrayItem = self::getResultString($arrayItem);
        }
        return $arrayData;
    }

    /**
     * @param string $string
     * @return string
     * getResultString
     */
    private static function getResultString(string $string):string
    {
        if(!preg_match_all("/(@{[\w\-._]+})/", $string, $match)) return $string;
        $search                 = [];
        $replace                = [];
        foreach($match[1] as $val){
            $searchKey          = '\''.$val.'\'';
            if(in_array($searchKey, $search, true)) continue;
            $search[]           = $searchKey;
            switch($val){
                case '@{ROOT_PATH}':{
                    $replace[]  = Boot::getRootPath();
                    break;
                }
                case '@{APPS_PATH}':{
                    $replace[]  = Boot::getAppsPath();
                    break;
                }
                case '@{COMPOSER_PATH}':{
                    $replace[]  = Boot::getComposerPath();
                    break;
                }
                case '@{CONF_PATH}':{
                    $replace[]  = Boot::getConfPath();
                    break;
                }
                case '@{LIBS_PATH}':{
                    $replace[]  = Boot::getLibsPath();
                    break;
                }
                case '@{DATA_PATH}':{
                    $val        = '@{PRO.dataPath}';
                    $replace[]  = self::getString(substr($val, 2, strlen($val) - 3), false);
                    break;
                }
                case '@{LOGS_PATH}':{
                    $replace[]  = Boot::getLogsPath();
                    break;
                }
                case '@{TMP_PATH}':{
                    $replace[]  = Boot::getTmpPath();
                    break;
                }
                default:{
                    $replace[]  = self::getString(substr($val, 2, strlen($val) - 3), false);
                    break;
                }
            }
        }
        if(count($search) > 0) $string = preg_replace($search, $replace, $string);
        return $string;
    }

    /**
     * @param array $cliArgvList
     * @return void
     * setCliArgvList
     */
    public static function setCliArgvList(array $cliArgvList):void
    {
        self::$cliArgvList          = $cliArgvList;
    }

    /**
     * @return array
     * getCliArgvList
     */
    public static function getCliArgvList():array
    {
        return self::$cliArgvList;
    }

    /**
     * @return PhpConf
     * getPhpConf
     */
    public static function getPhpConf():PhpConf
    {
        if(self::$PhpConf instanceof PhpConf) return self::$PhpConf;
        self::$PhpConf              = new PhpConf(self::getGroupList(PhpConf::PREFIX, false, true));
        return self::$PhpConf;
    }

    /**
     * @return ProjectConf
     * getProjectConf
     */
    public static function getProjectConf():ProjectConf
    {
        if(self::$ProjectConf instanceof ProjectConf) return self::$ProjectConf;
        self::$ProjectConf          = new ProjectConf(self::getGroupList(ProjectConf::PREFIX, false, true));
        return self::$ProjectConf;
    }

    /**
     * @return LogsConf
     * getLogsConf
     */
    public static function getLogsConf():LogsConf
    {
        if(self::$LogsConf instanceof LogsConf) return self::$LogsConf;
        self::$LogsConf             = new LogsConf(self::getGroupList(LogsConf::PREFIX, false, true));
        return self::$LogsConf;
    }

    /**
     * @return LangConf
     * getLangConf
     */
    public static function getLangConf():LangConf
    {
        if(self::$LangConf instanceof LangConf) return self::$LangConf;
        self::$LangConf             = new LangConf(self::getGroupList(LangConf::PREFIX, false, true));
        return self::$LangConf;
    }

    /**
     * @return WebSocketConf
     * getWebSocketConf
     */
    public static function getWebSocketConf():WebSocketConf
    {
        if(self::$WebSocketConf instanceof WebSocketConf) return self::$WebSocketConf;
        self::$WebSocketConf        = new WebSocketConf(self::getGroupList(WebSocketConf::PREFIX, false, true));
        return self::$WebSocketConf;
    }

    /**
     * @return ResponseConf
     * getResponseConf
     */
    public static function getResponseConf():ResponseConf
    {
        if(self::$ResponseConf instanceof ResponseConf) return self::$ResponseConf;
        self::$ResponseConf         = new ResponseConf(self::getGroupList(ResponseConf::PREFIX, false, true));
        return self::$ResponseConf;
    }

    /**
     * @return TokenKeyConf
     * getTokenKeyConf
     */
    public static function getTokenKeyConf():TokenKeyConf
    {
        if(self::$TokenKeyConf instanceof TokenKeyConf) return self::$TokenKeyConf;
        self::$TokenKeyConf         = new TokenKeyConf(self::getGroupList(TokenKeyConf::PREFIX, false, true));
        return self::$TokenKeyConf;
    }

    /**
     * @return SmartyConf
     * getSmartyConf
     */
    public static function getSmartyConf():SmartyConf
    {
        if(self::$SmartyConf instanceof SmartyConf) return self::$SmartyConf;
        self::$SmartyConf         = new SmartyConf(self::getGroupList(SmartyConf::PREFIX, false, true));
        return self::$SmartyConf;
    }

    /**
     * @return RouteConfGroup
     * getRouteConfGroup
     */
    public static function getRouteConfGroup():RouteConfGroup
    {
        if(self::$RouteConfGroup instanceof RouteConfGroup) return self::$RouteConfGroup;
        self::$RouteConfGroup       = new RouteConfGroup();
        return self::$RouteConfGroup;
    }

    /**
     * @return CacheConfGroup
     * getCacheConfGroup
     */
    public static function getCacheConfGroup():CacheConfGroup
    {
        if(self::$CacheConfGroup instanceof CacheConfGroup) return self::$CacheConfGroup;
        self::$CacheConfGroup       = new CacheConfGroup();
        return self::$CacheConfGroup;
    }

    /**
     * @return DbConfGroup
     * getDbConfGroup
     */
    public static function getDbConfGroup():DbConfGroup
    {
        if(self::$DbConfGroup instanceof DbConfGroup) return self::$DbConfGroup;
        self::$DbConfGroup      = new DbConfGroup();
        return self::$DbConfGroup;
    }

    /**
     * @return NetworkConfGroup
     * getNetworkConf
     */
    public static function getNetworkConfGroup():NetworkConfGroup
    {
        if(self::$NetworkConfGroup instanceof NetworkConfGroup) return self::$NetworkConfGroup;
        self::$NetworkConfGroup = new NetworkConfGroup();
        return self::$NetworkConfGroup;
    }
}